package com.vite.web.controller.common;

import com.vite.common.core.domain.AjaxResult;
import com.vite.common.exception.ServiceException;
import com.vite.common.utils.StringUtils;
import com.vite.file.util.MimeTypeUtils;
import com.vite.file.util.XFileUtils;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.FileStorageService;
import org.dromara.x.file.storage.core.ProgressListener;
import org.dromara.x.file.storage.core.upload.FilePartInfo;
import org.dromara.x.file.storage.core.upload.MultipartUploadSupportInfo;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.List;

/**
 * 通用请求处理
 *
 * @author vite
 */
@RestController
@RequestMapping("/common")
@RequiredArgsConstructor
@Slf4j
public class CommonController
{

    //x-file Storage服务
    private final FileStorageService fileStorageService;

    //分隔符
    private static final String FILE_DELIMITERS = ",";

    /**
     * 通用下载请求, 存在感较低的方法
     * @param fileName 文件名称
     * @param delete 是否删除
     */
    @GetMapping("/download")
    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response) throws Exception {
        if (!ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, XFileUtils.getFileType(fileName))) {
            throw new Exception(String.format("文件名称(%s)非法，不允许下载。 ", fileName));
        }
        // 获取文件信息
        FileInfo fileInfo = fileStorageService.getFileInfoByUrl(fileName);
        fileStorageService.download(fileInfo).outputStream(response.getOutputStream());
        if (delete)
        {
            fileStorageService.delete(fileInfo);
        }
    }

    /**
     * 通用上传请求（单个）
     */
    @PostMapping("/upload")
    public AjaxResult uploadFile(MultipartFile file) throws Exception{
        //验证文件
        XFileUtils.validFile(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        FileInfo fileInfo = fileStorageService.of(file)
                .setPath(XFileUtils.getPath())
                .setSaveFilename(XFileUtils.getFileName(file.getOriginalFilename()))
                .upload();
        AjaxResult ajax = AjaxResult.success();
        ajax.put("url", fileInfo.getUrl());
        ajax.put("fileName", file.getName());
        ajax.put("newFileName", fileInfo.getFilename());
        ajax.put("originalFilename", file.getOriginalFilename());
        return ajax;
    }

    /**
     * 通用上传请求（多个）
     */
    @PostMapping("/uploads")
    public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception{
            List<String> urlList = new ArrayList<>();
            List<String> fileNameList = new ArrayList<>();
            List<String> newFileNameList = new ArrayList<>();
            List<String> originalFileNameList = new ArrayList<>();
            for (MultipartFile file : files) {
                XFileUtils.validFile(file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
                FileInfo fileInfo = fileStorageService.of(file)
                        .setPath(XFileUtils.getPath())
                        .setSaveFilename(XFileUtils.getFileName(file.getOriginalFilename()))
                        .upload();
                urlList.add(fileInfo.getUrl());
                fileNameList.add(file.getName());
                newFileNameList.add(fileInfo.getFilename());
                originalFileNameList.add(file.getOriginalFilename());
            }
            AjaxResult ajax = AjaxResult.success();
            ajax.put("urls", StringUtils.join(urlList, FILE_DELIMITERS));
            ajax.put("fileNames", StringUtils.join(fileNameList, FILE_DELIMITERS));
            ajax.put("newFileNames", StringUtils.join(newFileNameList, FILE_DELIMITERS));
            ajax.put("originalFilenames", StringUtils.join(originalFileNameList, FILE_DELIMITERS));
            return ajax;
    }

    /**
     * 分片上传初始化
     * @param fileName 文件名
     * @param size 文件大小
     */
    @PostMapping("/initUploadPart")
    public AjaxResult initPartUpload(@RequestParam("fileName") String fileName,
                                     @RequestParam("size") Long size) throws Exception{
        MultipartUploadSupportInfo supportInfo = fileStorageService.isSupportMultipartUpload();
        if (!supportInfo.getIsSupport()) {
            throw new RuntimeException("此存储类型不支持分片上传!");
        }
        //验证文件合法性
        XFileUtils.validPartFile(fileName, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION);
        FileInfo fileInfo = fileStorageService
                .initiateMultipartUpload()
                .setPath(XFileUtils.getPath())
                .setSaveFilename(XFileUtils.getFileName(fileName))
                .setOriginalFilename(fileName)
                .setSize(size)
                .init();
        log.info("手动分片上传文件初始化成功：{}", fileInfo);
        AjaxResult ajax = AjaxResult.success();
        ajax.put("url",fileInfo.getUrl());
        ajax.put("uploadId",fileInfo.getUploadId());
        return ajax;
    }

    /**
     * 分片上传
     * @param file 分片文件
     * @param chunkNumber 分片数
     * @param url 文件地址
     * @param uploadId 文件uploadId
     */
    @PostMapping("/uploadChunk")
    public AjaxResult uploadChunk(@RequestParam("file") MultipartFile file,  //分片文件
                                 @RequestParam("chunkNumber") int chunkNumber,  //分片数
                                 @RequestParam("url") String url,
                                 @RequestParam("uploadId") String uploadId) throws Exception{

        // 获取文件信息
        FileInfo fileInfo = fileStorageService.getFileInfoByUrl(url);
        if(fileInfo == null){
            throw new ServiceException("文件未初始化或不存在");
        }
        // 校验文件uploadId
        if(!uploadId.equals(fileInfo.getUploadId())){
            fileStorageService.abortMultipartUpload(fileInfo);
            throw new ServiceException("分片文件校验失败");
        }
        FilePartInfo filePartInfo = fileStorageService
            .uploadPart(fileInfo, chunkNumber, file, file.getSize())
            .setProgressListener(new ProgressListener() {
                @Override
                public void start() {
                    log.info("分片 " + chunkNumber + " 上传开始");
                }
                @Override
                public void progress(long progressSize, Long allSize) {
                    log.info("已上传 " + progressSize + " 总大小" + (allSize == null ? "未知" : allSize));
                }
                @Override
                public void finish() {
                    log.info("分片 " + chunkNumber + " 上传结束");
                }
            })
            .setHashCalculatorMd5()
            .upload();
        log.info("手动分片上传-分片上传成功：{}", filePartInfo);
        return AjaxResult.success().put("chunkNumber",chunkNumber);
    }

    /**
     * 文件合并
     * @param chunkNumber 分片数
     * @param totalChunks 分片总数
     * @param url 文件地址
     * @param uploadId 文件uploadId
     */
    @PostMapping("/completeUpload")
    public AjaxResult completeUpload(@RequestParam("chunkNumber") int chunkNumber,
                                     @RequestParam("totalChunks") int totalChunks,
                                     @RequestParam("url") String url,
                                     @RequestParam("uploadId") String uploadId) throws Exception{
        if (chunkNumber != totalChunks) {
            throw new ServiceException("分片未正确上传");
        }
        // 获取文件信息
        FileInfo fileInfo = fileStorageService.getFileInfoByUrl(url);
        if(fileInfo == null){
            throw new ServiceException("文件未初始化或不存在");
        }
        // 校验文件uploadId
        if(!uploadId.equals(fileInfo.getUploadId())){
            fileStorageService.abortMultipartUpload(fileInfo);
            throw new ServiceException("分片文件校验失败");
        }
        //合并文件
        fileStorageService.completeMultipartUpload(fileInfo)
            .setProgressListener(new ProgressListener() {
                @Override
                public void start() {
                    log.info("文件合并开始");
                }
                @Override
                public void progress(long progressSize, Long allSize) {
                    log.info("已合并 " + progressSize + " 总大小" + (allSize == null ? "未知" : allSize));
                }
                @Override
                public void finish() {
                    log.info("文件合并结束");
                }
            })
            .complete();
        log.info("手动分片上传文件完成成功：{}", fileInfo);
        return AjaxResult.success();
    }

}
